package com.example.tool.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.CellExtra;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.example.tool.bean.ExcelMergeHandler;
import com.example.tool.bean.ZopDO;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.List;

@Slf4j
public class ExportUtil {
    /**
     * 导出 Excel
     *
     * @param response  :响应
     * @param data      :导出的list集合
     * @param fileName  :Excel名（最好英文，无需后缀）
     * @param sheetName :sheet页名
     * @param clazz     :导出Excel实体类
     * @throws Exception
     */
    public static void writeExcel(HttpServletResponse response, List<? extends Object> data, String fileName, String sheetName, Class clazz) throws Exception {
//        //表头样式
//        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
//        //设置表头居中对齐
//        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
//        //内容样式
//        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
//        //设置内容靠左对齐
//        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
//        HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
//
//        EasyExcel.write(getOutputStream(fileName, response), clazz)
//                .excelType(ExcelTypeEnum.XLSX)//读取的文件类型
//                .sheet(sheetName)//读取的sheet,可以是行号也可以是sheet名
//                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())//设置自动适应宽度
//                .registerWriteHandler(horizontalCellStyleStrategy)//设置样式(或：registerWriteHandler(createStyleStrategy))
//                .doWrite(data);

        // 1. 设置响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));

        // 2. 获取 OutputStream（不要手动关闭！）
        OutputStream out = response.getOutputStream();

        // 3. 设置样式
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
        HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);

        // 4. 写入 Excel
        try {
            EasyExcel.write(out, clazz)
                    .excelType(ExcelTypeEnum.XLSX)
                    .sheet(sheetName)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .registerWriteHandler(styleStrategy)
                    .doWrite(data);
        } finally {
            // 5. 确保流正确关闭（EasyExcel 3.x 会自动关闭，但可以手动确保）
            if (out != null) {
                out.close();

            }
        }
    }

    /**
     * 格式处理
     */
    private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception {
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setCharacterEncoding("utf8");
        response.addHeader("Content-disposition", "attachment;filename=" + fileName + ExcelTypeEnum.XLSX.getValue());
        return response.getOutputStream();
    }

    /**
     * 自定义样式
     */
    private static HorizontalCellStyleStrategy createStyleStrategy() {
        // 头的策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 背景设置为红色
        headWriteCellStyle.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short) 10);
        headWriteCellStyle.setWriteFont(headWriteFont);
        // 内容的策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);  //底边框
        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);  //左边框
        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);  //右边框
        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);  //顶边框
        WriteFont contentWriteFont = new WriteFont();
        // 字体大小
        contentWriteFont.setFontHeightInPoints((short) 10);
        contentWriteCellStyle.setWriteFont(contentWriteFont);
        // 执行策略
        HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);

        return horizontalCellStyleStrategy;
    }


    /**
     * 处理合并单元格
     *
     * @param data               解析数据
     * @param extraMergeInfoList 合并单元格信息
     * @param headRowNumber      起始行
     * @return 填充好的解析数据
     */

    private List<T> explainMergeData(List<T> data, List<CellExtra> extraMergeInfoList, Integer headRowNumber) {
        //循环所有合并单元格信息
        extraMergeInfoList.forEach(cellExtra -> {
            //获取第一行
            int firstRowIndex = cellExtra.getFirstRowIndex() - headRowNumber;
            //获取最后一行
            int lastRowIndex = cellExtra.getLastRowIndex() - headRowNumber;
            //获取第一列
            int firstColumnIndex = cellExtra.getFirstColumnIndex();
            //获取最后一列
            int lastColumnIndex = cellExtra.getLastColumnIndex();
            //获取初始值
            Object initValue = getInitValueFromList(firstRowIndex, firstColumnIndex, data);
            //设置值
            for (int i = firstRowIndex; i <= lastRowIndex; i++) {
                for (int j = firstColumnIndex; j <= lastColumnIndex; j++) {
                    setInitValueToList(initValue, i, j, data);
                }
            }
        });
        return data;
    }

    /**
     * 设置合并单元格的值
     *
     * @param filedValue  值
     * @param rowIndex    行
     * @param columnIndex 列
     * @param data        解析数据
     */

    private void setInitValueToList(Object filedValue, Integer rowIndex, Integer columnIndex, List<T> data) {
        T object = data.get(rowIndex);

        for (Field field : object.getClass().getDeclaredFields()) {
            //提升反射性能，关闭安全检查
            field.setAccessible(true);
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation != null) {
                if (annotation.index() == columnIndex) {
                    try {
                        field.set(object, filedValue);
                        break;
                    } catch (IllegalAccessException e) {
                        log.error("设置合并单元格的值异常：{}", e.getMessage());
                    }
                }
            }
        }
    }

    /**
     * 获取合并单元格的初始值
     * rowIndex对应list的索引
     * columnIndex对应实体内的字段
     *
     * @param firstRowIndex    起始行
     * @param firstColumnIndex 起始列
     * @param data             列数据
     * @return 初始值
     */

    private Object getInitValueFromList(Integer firstRowIndex, Integer firstColumnIndex, List<T> data) {
        Object filedValue = null;
        T object = data.get(firstRowIndex);
        for (Field field : object.getClass().getDeclaredFields()) {
            //提升反射性能，关闭安全检查
            field.setAccessible(true);
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation != null) {
                if (annotation.index() == firstColumnIndex) {
                    try {
                        filedValue = field.get(object);
                        break;
                    } catch (IllegalAccessException e) {
                        log.error("设置合并单元格的初始值异常：{}", e.getMessage());
                    }
                }
            }
        }
        return filedValue;
    }

    public static void exportData(HttpServletResponse response, List<ZopDO> list, ZopDO clazz) throws IOException {
        String fileName = "快递物流.xlsx";
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setContentType("application/json;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        response.addHeader("Pargam", "no-cache");
        response.addHeader("Cache-Control", "no-cache");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
        ServletOutputStream output = response.getOutputStream();
        //需要合并的列
        int[] mergeColumeIndex = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
        // 从第二行后开始合并
        int mergeRowIndex = 0;
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 背景设置为白色
        headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

        // 内容的策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
        //contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
        // 背景绿色
        //contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        //设置 自动换行
        contentWriteCellStyle.setWrapped(true);

        //设置 垂直居中
        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        //设置边框线
        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);   // 设置单元格上边框为细线
        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);// 设置单元格下边框为粗线
        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);     // 设置单元格左边框为中线
        contentWriteCellStyle.setBorderRight(BorderStyle.THIN); // 设置单元格右边框为中虚线
        //设置 水平居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
        HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);

        EasyExcel.write(output, clazz.getClass()).sheet("快递物流")
                .head(clazz.getClass())
                .registerWriteHandler(new ExcelMergeHandler(mergeRowIndex, mergeColumeIndex))
                .registerWriteHandler(horizontalCellStyleStrategy)
                .registerWriteHandler(new AbstractColumnWidthStyleStrategy() {
                    @Override
                    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<com.alibaba.excel.metadata.data.WriteCellData<?>> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
                        Sheet sheet = writeSheetHolder.getSheet();
                        int columnIndex = cell.getColumnIndex();
                        if (columnIndex == 5) {
                            // 列宽100
                            sheet.setColumnWidth(columnIndex, 10000);
                            // 行高40
                            sheet.setDefaultRowHeight((short) 5000);
                        } else {
                            // 列宽50
                            sheet.setColumnWidth(columnIndex, 5000);
                            // 行高40
                            sheet.setDefaultRowHeight((short) 4000);
                        }

                    }
                })
                .doWrite(list);
        output.flush();

    }


}